package com.xzzz.common.wechat.manager;

import cn.hutool.core.util.StrUtil;
import com.xzzz.common.base.util.json.JsonUtil;
import com.xzzz.common.wechat.WxConstant;
import com.xzzz.common.wechat.WxProperties;
import com.xzzz.common.wechat.log.WxLog;
import com.xzzz.common.wechat.pojo.WxAccessTokenDTO;
import com.xzzz.common.wechat.pojo.WxCode2SessionDTO;
import com.xzzz.common.wechat.pojo.WxPhoneNumberDTO;
import com.xzzz.common.wechat.security.WxEncryptedDataUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;

/**
 * 微信相关API
 *
 * @author xzzz
 * 
 */
@Slf4j
@Component
public class WxAuthManager extends AbstractWxManager {

    private String APP_ID;
    private String APP_SECRET;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private WxProperties properties;

    @PostConstruct
    public void init() {
        APP_ID = properties.getMp().getAppId();
        APP_SECRET = properties.getMp().getAppSecret();
    }

    private static final String TEST_CODE = "test_code";
    private static final String TEST_ENCRYPTED_DATA = "test_encrypted_data";
    private static final String TEST_OPEN_ID = "test_openid_" + "13901000001";
    private static final String TEST_PHONE = "13901000001";

    /**
     * 用户登录
     * 根据 CODE 获取 OPENID 和 会话秘钥
     */
    @WxLog(type = "mp", tag = "authCode2Session", desc = "通过code获取微信用户信息")
    @Cacheable(cacheNames = WxConstant.AUTH_CODE_KEY, key = "#code", unless = "#result == null")
    public WxCode2SessionDTO authCode2Session(String code) {
        // 如果使用的是 test_code, 则 mock 数据
        if (TEST_CODE.equals(code)) {
            return new WxCode2SessionDTO().setOpenid(TEST_OPEN_ID).setUnionid(TEST_OPEN_ID);
        }

        // 真实数据
        String body = getBody(WxConstant.AUTH_CODE2SESSION_URL, properties.getMp().getAppId(), APP_SECRET, code);
        WxCode2SessionDTO wxCode2SessionDTO = JsonUtil.toObj(body, WxCode2SessionDTO.class);
        checkResult(wxCode2SessionDTO);
        return wxCode2SessionDTO;
    }


    /**
     * 从加密信息中获取手机号信息
     *
     * @param sessionKey    sessionKey
     * @param iv            iv
     * @param encryptedData 加密数据
     * @return 解密后对象
     */
    public WxPhoneNumberDTO getWxPhoneNumberDTO(String sessionKey, String iv, String encryptedData) {
        if (TEST_ENCRYPTED_DATA.equals(encryptedData)) {
            WxPhoneNumberDTO wxPhone = new WxPhoneNumberDTO();
            wxPhone.setPhoneNumber(TEST_PHONE);
            return wxPhone;
        }
        return WxEncryptedDataUtil.decryptPhone(APP_ID, sessionKey, iv, encryptedData);
    }


    /**
     * 刷新微信接口调用凭证
     *
     * @return 微信接口调用凭证
     */
    @WxLog(type = "mp", tag = "authCode2Session", desc = "'[' + #appId + ']刷新微信接口调用凭证'")
    public String refreshAuthAccessToken(String appId) {
        String body = getBody(WxConstant.AUTH_GET_ACCESS_TOKEN_URL, APP_ID, APP_SECRET);
        log.info("[微信模块] 刷新微信调用凭证: {}", body);
        WxAccessTokenDTO accessToken = JsonUtil.toObj(body, WxAccessTokenDTO.class);
        checkResult(accessToken);
        setAuthAccessTokenToCache(accessToken, APP_ID);
        return accessToken.getAccess_token();
    }


    /**
     * 获取微信接口调用凭证, 如果凭证不存在, 会刷新后获取刷新结果
     *
     * @return 微信接口调用凭证
     */
    @Override
    public String getAuthAccessToken(String appId) {
        String accessToken = stringRedisTemplate.opsForValue().get(WxConstant.ACCESS_TOKEN_KEY + APP_ID);
        if (StrUtil.isBlank(accessToken)) {
            accessToken = refreshAuthAccessToken(APP_ID);
        }
        return accessToken;
    }


    /**
     * 设置微信接口调用凭证到缓存
     *
     * @param accessToken 微信接口调用凭证
     */
    public void setAuthAccessTokenToCache(WxAccessTokenDTO accessToken, String appId) {
        stringRedisTemplate.opsForValue().set(
                WxConstant.ACCESS_TOKEN_KEY + APP_ID,
                accessToken.getAccess_token(),
                accessToken.getExpires_in(),
                TimeUnit.SECONDS
        );
    }

}

